import ora from 'ora'
import path from 'path'
import config from '@/config'
import { Connect } from '@/connect'
import { formatDuration } from '@/utils/format'
import { getErrorMessage } from '@/utils/error'
import { FileDiffAction } from './actions/fileDiff'
import { FileSendAction } from './actions/fileSend'
import { passwordInputSpinner } from '@/utils/spinner'
import { UpdateFileAction } from './actions/updateFile'
import { InitiateVerifyAction } from '@/connect/actions/verify'
import type { Send } from '.'
import type { Socket } from 'net'
import type { VerifyInfo } from '@/connect/types'

export class SendConnect extends Connect {
  constructor(socket: Socket, send: Send) {
    super(socket, path.join(config.globalLogDir, 'connect/send'))
    this.sendInstance = send
    this.initAction()

    this.outputError = false

    this.spinner.start('Connecting...')

    this.on('end', () => {
      this.spinner.clear()
      this.spinner.stop()
    })

    this.on('connect-failure', () => {
      this.spinner.fail('Connection failed')
      process.exit(1)
    })

    this.on('error', (err) => {
      this.spinner.fail(getErrorMessage(err))
    })
  }

  readonly sendInstance: Send

  readonly spinner = ora()

  setSpinnerText(text: string) {
    this.spinner.text = text
    if (this.socket && !this.socket.closed && !this.spinner.isSpinning) {
      this.spinner.start()
    }
  }

  setVerifyInfo(info: VerifyInfo) {
    this.store.set('verifyInfo', info)
  }

  initAction() {
    this.addAction(new InitiateVerifyAction(this))
    this.addAction(new FileDiffAction(this))
    this.addAction(new FileSendAction(this))
    this.addAction(new UpdateFileAction(this))

    this.runAction('initiateConnect')
      .then(async () => {
        this.spinner.succeed('Connection successful')

        let password = this.sendInstance.currentConfig.password
        if (!password) {
          this.startIntervalSendPong(3000)

          const { result, destroy } = passwordInputSpinner(this.spinner, {
            restart: this.spinner.isSpinning
          })

          const off = this.onceReturnOff('end', () => {
            destroy()
            this.stopIntervalSendPong()
            clearTimeout(timer)
            this.spinner.fail('disconnect')
          })

          const timer = setTimeout(
            () => {
              destroy()
              this.stopIntervalSendPong()
              off()
              this.spinner.warn('timeout')
            },
            2.9 * 60 * 1000
          )

          password = await result.finally(() => {
            this.stopIntervalSendPong()
          })

          off()
          clearTimeout(timer)
        }
        this.setVerifyInfo({
          command: 'send',
          name: this.sendInstance.currentConfig.name,
          password
        })
      })
      .then(() => this.runAction('initiateVerify'))
      .then(() => this.runAction('fileDiff'))
      .then((name) => {
        if (name === 'fileSend') {
          return this.runAction('fileSend')
        }
        return Promise.resolve(void 0)
      })
      .then(() => this.runAction('updateFile'))
      .then(() => {
        this.spinner.succeed(
          `Done ${formatDuration(Date.now() - this.sendInstance.startTime.getTime())}`
        )
      })
      .catch((err) => {
        this.spinner.fail(getErrorMessage(err))
      })
  }
}
